package routers

import (
	"WZ_law-api/cm"
	"context"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"strconv"

	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/log"
	//"sync"
)

var Ipfs = make(map[string]string)
var Trust = make(map[string]string)

//暂时仅封装chain对象
type ChainHandler struct {
	chain *cm.ClientManager

	//http request event
	// uploadCh chan *ChainRequest
	// resultCh chan *uploadEvent

	//trust time cache
	//trustMux  sync.Mutex
	//exitTrust map[string]*trust.ProofTime

	//upload request status
	// pendingTask    map[string]int
	// pendingTaskMux sync.Mutex

	// quit chan struct{}
}

//ChainRequest receive upload-data request from accounts
type ChainRequest struct {
	//Url string `json:"url"`
	//Privkey string              `json:"private_key"`
	//Data []*ChainRequestData `json:"data"`
	//API请求的数据切片
	Data []*ChainRequestData `json:"data"`
}

type ChainRequestData struct {
	//AppID       string `json:"app_id"`
	//AccessToken string `json:"access_token"`
	UserId       string `json:"user_id"`
	FileID       string `json:"file_id"`
	Sha256       string `json:"sha256"`
	Sm3          string `json:"sm3"`
	Md5          string `json:"md5"`
	Timestamp    string `json:"request_timestamp"`
	ChainId      string `json:"chain_id"`
	ContractAddr string `json:"contract_addr"`
	IpfsHash     string `json:"ipfs_hash"`
}

//ChainResponse return status about upload request
//API回应的格式
type ChainResponse struct {
	Code int          `json:"code"` //错误码
	Msg  string       `json:"msg"`  //成功or失败
	Data []*ChainData `json:"data"` //具体的respons数据
}

type ChainData struct {
	FileID          string         `json:"file_id"`
	BlockNumber     string         `json:"block_height"`
	BlockHash       string         `json:"block_hash"`
	TransactionHash string         `json:"tran_Hash"`
	ContractAddress common.Address `json:"contract_addr"`
	TrustTime       string         `json:"trust_timestamp"`
	Timestamp       string         `json:"req_timestamp"`
}

type BlockData struct {
	Timestamp string `json:"timestamp"`
}

// type ChainDataResponse struct {
// 	Code int    `json:"code"`
// 	Msg  string `json:"msg"`
// 	//Data []*ChainData `json:"data"`
// 	Data *ChainData `json:"data"`
// }

//type uploadEvent struct {
// 	url  string
// 	data []*ChainData
// }

//ChainDataResponse return data should be posted to callback url

//ChainData return picoChain result after upload request successfully.

// type Receipt struct {
// }
func NewChainHandler(chain *cm.ClientManager) *ChainHandler {
	ch := &ChainHandler{
		chain: chain,
		//uploadCh:    make(chan *ChainRequest, 1000),
		//resultCh:    make(chan *uploadEvent, 1000),
		//pendingTask: make(map[string]int),
		//exitTrust:   make(map[string]*trust.ProofTime),
		// quit: make(chan struct{}),
	}

	//go ch.loop()
	return ch
}

func (ch *ChainHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.Method == "POST" {

		w.Header().Add("Content-Type", "application/json;charset=utf-8")

		b, err := ioutil.ReadAll(r.Body)
		if err != nil {
			log.Warn("chainHandler", "err", err)
			w.Write(encRespon(newChainResponse(ErrHTTPRequest, nil)))
			return
		}
		defer r.Body.Close()

		//parse request
		req := new(ChainRequest)
		//解析API请求inputdata到ChainRequest对象中
		if err = json.Unmarshal(b, req); err != nil {
			log.Warn("chainHandler", "err", err)
			w.Write(encRespon(newChainResponse(ErrHTTPRequest, nil)))
			return
		}

		//check callback url
		// if req.Url == "" {
		// 	log.Warn("chainHandler", "err", "empty callback")
		// 	w.Write(encRespon(newChainResponse(ErrHTTPRequest)))
		// 	return
		// }

		//check key
		//if succeed := ch.chain.CheckPrivateKey(req.Privkey); succeed != true {
		//	log.Warn("chainHandler", "err", err)
		//	w.Write(encRespon(newChainResponse(ErrAccKey)))
		//	return
		//}

		//check chain data
		for _, dat := range req.Data {
			//inputdata 参数判断
			if dat.FileID == "" || dat.Sha256 == "" || dat.Sm3 == "" || dat.Md5 == "" || dat.Timestamp == "" {
				log.Warn("chainHandler", "err", "empty chaindata")
				w.Write(encRespon(newChainResponse(ErrChainData, nil)))
				return
			}
		}

		// if req.Data.FileID == "" || req.Data.Sha256 == "" || req.Data.Sm3 == "" || req.Data.Md5 == "" || req.Data.Timestamp == "" {
		// 	log.Warn("chainHandler", "err", "empty chaindata")
		// 	w.Write(encRespon(newChainResponse(ErrChainData, nil)))
		// 	return
		// }

		//post upload request event
		respond := ch.handleUpload(req) //调用合约Add函数，但会output
		if respond == nil {
			w.Write(encRespon(newChainResponse(ErrChainData, nil)))
			return
		}
		//转换为json格式并返回
		w.Write(encRespon(newChainResponse(nil, respond)))

	} else {
		log.Warn("chainHandler", "unknown request", r.Method)
		w.Write(encRespon(newChainResponse(ErrHTTPRequest, nil)))
	}
}

//PostUploadReq post pending upload request event.
// func (ch *ChainHandler) PostUploadReq(req *ChainRequest) error {
// 	select {
// 	case ch.uploadCh <- req:
// 	case <-ch.quit:
// 	}
// 	return nil
// }

//newChainResponse return result code and its error string
func newChainResponse(err error, data *ChainData) *ChainResponse {
	//check upload result
	code := checkUploadErrCode(err)

	//if code isn't 0, return its error message
	msg := "成功"
	if code != 0 {
		msg = err.Error()
	}
	res := &ChainResponse{
		Code: code,
		Msg:  msg,
		Data: make([]*ChainData, 0),
	}
	res.Data = append(res.Data, data)
	return res
}

func (ch *ChainHandler) handleUpload(req *ChainRequest) *ChainData {
	//upload data to the picoChain
	//虽然传入的是请求数组，但目前只考虑一次只有一个请求，不会有多个请求同时到达
	//调用Add函数
	tx, receipt, err := ch.chain.Contract.Add(req.Data[0].UserId, req.Data[0].FileID, req.Data[0].Sha256, req.Data[0].Sm3, req.Data[0].Md5, req.Data[0].Timestamp)
	_ = tx
	//调用失败或者status不为0
	if err != nil || receipt.Status != 0 {
		log.Warn("upload data", "fileid", req.Data[0].FileID, "receipt.Status", receipt.Status, "err", err)
		//chDat := make([]*ChainData, 1)
		//dat := &ChainData{FileID: req.Data[0].FileID}
		//return dat
		//chDat[0] = dat
		return nil
	}
	Ipfs[req.Data[0].FileID] = req.Data[0].IpfsHash
	//组装返回的数据
	dat := &ChainData{
		FileID:          req.Data[0].FileID,
		BlockNumber:     receipt.BlockNumber,
		BlockHash:       receipt.BlockHash,
		TransactionHash: receipt.TransactionHash,
		ContractAddress: common.HexToAddress(cm.Contractaddr),
		Timestamp:       req.Data[0].Timestamp,
	}
	blockint64, err := strconv.ParseInt(receipt.BlockNumber, 10, 64)
	if err != nil {
		log.Warn("BlockNumber To Int64 err:", err)
	}
	blockdata, err := ch.chain.Client.GetBlockByNumber(context.Background(), blockint64, true)
	if err != nil {
		log.Warn("GetBlockByNumber err:", err)
	}

	bd := new(BlockData)
	err = json.Unmarshal(blockdata, bd)
	log.Info("blockdata timestamp:", bd.Timestamp)
	if err != nil {
		log.Warn("BlockData Unmarshal err:", err)
	}
	Trust[req.Data[0].FileID] = bd.Timestamp
	dat.TrustTime = bd.Timestamp
	// ch.setTrustTime(dat)
	// chDat[0] = dat
	//post upload result,update pendig task
	//ch.PostUploadRes(req.Url, chDat)
	return dat
}

//setTrustTime new trust timestamp for proofing block data really exits.
//it was empty in timestamp when return a error result.
// func (ch *ChainHandler) setTrustTime(data *ChainData) error {
// 	trustTime, err := trust.NewProofTime(data.Timestamp, data.BlockHash)
// 	if err != nil {
// 		log.Warn("SetTrustTime err:", err)
// 		return err
// 	}
// 	data.TrustTime = trustTime
// 	return nil
// }

//loop wait event
// func (ch *ChainHandler) loop() {
// 	for {
// 		select {
// 		case req := <-ch.uploadCh:
// 			go ch.handleUpload(req)

// 		case event := <-ch.resultCh:
// 			res := &ChainDataResponse{Data: event.data}

// 			//post until succeed or arrive 50 time.
// 			go CallbackResponse(event.url, res)

// 			//when exitTrust too large,clear it
// 			//if len(ch.exitTrust) >= 10000 {
// 			//	ch.exitTrust = make(map[string]*trust.ProofTime)
// 			//}

// 		case <-ch.quit:
// 			return
// 		}
// 	}
// }

// func (ch *ChainHandler) PostUploadRes(url string, data []*ChainData) error {
// 	select {
// 	case ch.resultCh <- &uploadEvent{url: url, data: data}:
// 	case <-ch.quit:
// 	}
// 	return nil
// }
